#include"Scrobbler.h" 

#include"LFMParser.h"
#include"LFMCorrection.h"
#include"LFMCrypter.h"
#include"LFMPostDataBuilder.h"
#include"MetaDataEditor.h"
#include"Settings.h"
#include"StatusBar.h"
#include"Song.h"

#include<QDesktopServices>

Scrobbler::Scrobbler(QObject *parent):QObject(parent){
	m_nowPlaying = 0;
	m_timeTillScrobbleRemaining = 0;
	m_startedAt = 0;
	m_reallyStartedAt = 0;
	m_buffer = 0;
	
	m_crypter = new LFMCrypter;
	m_postDataBuilder = new LFMPostDataBuilder(readSessionKey());
	
	m_timerNowPlaying = new QTimer(this);
	m_timerNowPlaying->setSingleShot(true);
	connect(m_timerNowPlaying, SIGNAL(timeout()), this, SLOT(slotSendNowPlaying()));
	
	m_timerScrobble = new QTimer(this);
	m_timerScrobble->setSingleShot(true);
	connect(m_timerScrobble, SIGNAL(timeout()), this, SLOT(slotSendScrobble()));
	
	m_networkAccessManager = new QNetworkAccessManager(this);
	m_networkReply = 0;
	Settings::instance()->lastfm->isScrobbling = isActive();
	connect(Settings::instance(), SIGNAL(lastfmChanged()), this, SLOT(slotSettingsChanged()));
}

Scrobbler::~Scrobbler(){
	m_timerNowPlaying->disconnect();
	m_timerScrobble->disconnect();
	if(m_crypter)
		delete m_crypter;
}

bool Scrobbler::isActive()const{
	return m_postDataBuilder->canScrobble();
}

void Scrobbler::slotNowPlaying(QPointer<Song> song){
	if(m_nowPlaying)
		m_nowPlaying->disconnect(this);
	printf("Scrobbler::slotNowPlaying, song valid: %d\n", song->isValid());
	if(song->duration() < 30000){
		m_timerNowPlaying->stop();
		m_timerScrobble->stop();
		m_nowPlaying = 0;
	}else{
		m_nowPlaying = song;
		m_startedAt = time(0);
		m_reallyStartedAt = m_startedAt;
		m_timerNowPlaying->start(DELAY_NOW_PLAYING);
		m_timerScrobble->start(scrobbleDelay(song));
		connect(m_nowPlaying, SIGNAL(deleted()), this, SLOT(slotCurrentSongDeleted()));
	}
}

#include<stdio.h>
/*void Scrobbler::slotPlayerStateChanged(Player::State state){
	switch(state){
		case Player::Playing:
			if(m_timeTillScrobbleRemaining > 0){
				m_timerScrobble->start(m_timeTillScrobbleRemaining);
				m_startedAt = time(0);
			}
			break;
		case Player::Paused:
			m_timeTillScrobbleRemaining = m_timerScrobble->interval() - (time(0) - m_startedAt)*1000;
			m_timerScrobble->stop();
			break;
		case Player::Stopped:
			m_timerNowPlaying->stop();
			m_timerScrobble->stop();
			m_nowPlaying = 0;
			break;
		
	}
}*/

void Scrobbler::slotCurrentSongDeleted(){
	m_nowPlaying = 0;
}

void Scrobbler::slotSendNowPlaying(){
	if(SCROBBLING_ENABLED){
		QString data = m_postDataBuilder->build("track.updatenowplaying", m_nowPlaying, LFMPostDataBuilder::ARTIST | LFMPostDataBuilder::TRACK | LFMPostDataBuilder::ALBUM | LFMPostDataBuilder::SESSION_KEY | LFMPostDataBuilder::SIGNATURE);
		if(data.isEmpty()){
			printf("Scrobble failed, empty data received.\n");
			return;
		}
		printf("SendNowPlaying()\n");
		printf("From PostDataBuilder: %s\n", data.toUtf8().data());

		if(m_buffer)
			delete m_buffer;
		m_buffer = new QBuffer;
		m_buffer->setData(data.toUtf8());
		QNetworkRequest request( QUrl("http://ws.audioscrobbler.com/2.0/") );
		request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
		m_networkReply = m_networkAccessManager->post(request, m_buffer);
		connect(m_networkReply, SIGNAL(readyRead()), this, SLOT(slotReplyReadyReadCorrection()));
	}
}

void Scrobbler::slotSendScrobble(){
	if(SCROBBLING_ENABLED){
		QString data = m_postDataBuilder->build(	"track.scrobble", 
						m_nowPlaying, 
						LFMPostDataBuilder::ARTIST | LFMPostDataBuilder::TRACK | LFMPostDataBuilder::TIMESTAMP | LFMPostDataBuilder::ALBUM | LFMPostDataBuilder::SESSION_KEY | LFMPostDataBuilder::SIGNATURE, 
						QString().setNum(m_reallyStartedAt));
		if(data.isEmpty()){
			printf("Scrobble failed, empty data received.\n");
			return;
		}
		printf("From PostDataBuilder: %s\n", data.toUtf8().data());
		printf("SendScrobble()\n");
		
		if(m_buffer)
			delete m_buffer;
		m_buffer = new QBuffer;
		m_buffer->setData(data.toUtf8());
		QNetworkRequest request( QUrl("http://ws.audioscrobbler.com/2.0/") );
		request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
		m_networkReply = m_networkAccessManager->post(request, m_buffer);
		connect(m_networkReply, SIGNAL(readyRead()), this, SLOT(slotReplyReadyRead()));
	}
}

//Received data appears to not be very interesting.
/*void Scrobbler::getTrackInfo(){
	QString data = m_postDataBuilder->build(	"track.getinfo",
							m_nowPlaying,
							LFMPostDataBuilder::TRACK | LFMPostDataBuilder::ARTIST);
	if(data.isEmpty()){
		printf("Retrieving trackInfo failed, empty data received.\n");
		return;
	}
	
	if(m_buffer)
		delete m_buffer;
	m_buffer = new QBuffer;
	m_buffer->setData(data.toUtf8());
	QNetworkRequest request( QUrl("http://ws.audioscrobbler.com/2.0/") );
	request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
	m_networkReply = m_networkAccessManager->post(request, m_buffer);
	connect(m_networkReply, SIGNAL(readyRead()), this, SLOT(slotReplyReadyRead()));
}*/

qint64 Scrobbler::scrobbleDelay(QPointer<Song> song){
	qint64 max = 240000;
	qint64 half = song->duration() / 2;
	if(max < half)
		return max;
	return half;
}

void Scrobbler::startSession(){
	getAuthenticationToken();
}

void Scrobbler::stopSession(){
	QFile file(Settings::instance()->settingsDir() + "encrypted_session");
	file.remove();
	m_postDataBuilder->setSessionKey(QString());
}

void Scrobbler::getAuthenticationToken(){
	QUrl url( QString("http://ws.audioscrobbler.com/2.0/?") + m_postDataBuilder->buildAuthData("auth.gettoken") );
	QNetworkRequest request(url);
	m_networkReply = m_networkAccessManager->get(request);
	connect(m_networkReply, SIGNAL(readyRead()), this, SLOT(slotReplyReadyReadToken()));
}

void Scrobbler::slotReplyReadyRead(){
	QByteArray ba = m_networkReply->readAll();
	printf("%s\n", ba.data());
	if(m_buffer){
		delete m_buffer;
		m_buffer = 0;
	}
}

void Scrobbler::slotReplyReadyReadCorrection(){
	if(m_buffer){
		delete m_buffer;
		m_buffer = 0;
	}
	
	QByteArray ba = m_networkReply->readAll();
	printf("Correction: %s\n", ba.data());
	LFMCorrection *correction = LFMParser::parseCorrection(ba);
	if(!correction){
// 		getTrackInfo();
		return;
	}
	for(LFMCorrection *c = correction; c != 0; c = c->next)
		c->print();
	if(correction->count() == 1)
		correction->path = m_nowPlaying->path();
	if(Settings::instance()->lastfm->offerCorrections)
		MetaDataEditor::launchEditorForCorrection(correction);
	delete correction;
}

void Scrobbler::slotReplyReadyReadSession(){
	QByteArray ba = m_networkReply->readAll();
	printf("%s\n", ba.data());
	QString session = LFMParser::parseSession(ba);
	if(session.isEmpty()){
		QMessageBox::critical(0, "Error", "Critical error during Last.FM authentication.");
		return;
	}
	writeSessionKey(session);
	m_postDataBuilder->setSessionKey(session);
}

void Scrobbler::slotReplyReadyReadToken(){
	QByteArray ba = m_networkReply->readAll();
	printf("%s\n", ba.data());
	QString token = LFMParser::parseToken(ba.data());
	printf("Token: %s\n", token.toUtf8().data());
	
	if(userAuthentication(token)){
		QString url = QString("http://ws.audioscrobbler.com/2.0/?") + m_postDataBuilder->buildAuthData("auth.getSession", token); 
		
		QNetworkRequest request( QUrl(url.toUtf8().data()) );
		m_networkReply = m_networkAccessManager->get(request);
		connect(m_networkReply, SIGNAL(readyRead()), this, SLOT(slotReplyReadyReadSession()));
	
	}else
		sStatusBar->log("Last.FM authentication cancelled by user.");
}

bool Scrobbler::userAuthentication(const QString &token){
	QUrl authenticationUrl( QString("http://www.last.fm/api/auth/?api_key=") + m_postDataBuilder->api_key() + ("&token=") + token );
	QString text;
	if( !QDesktopServices::openUrl( authenticationUrl ) ){
		//This shouldn't really happen, but better make sure we do our job neatly anyway.
		text = "Please visit " + authenticationUrl.toString() + " and allow access to Last.FM. Press \"Yes\" when done or \"No\" if refused.";
	}else{
		text = "Please allow access to Last.FM. Press \"Yes\" when done or \"No\" if refused.";
	}
	return QMessageBox::question( (QWidget*)parent(), "Authentication process", text, QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes ) == QMessageBox::Yes;
}

QString Scrobbler::readSessionKey(){
	return m_crypter->decryptFromFile(Settings::instance()->settingsDir() + "encrypted_session");
}

void Scrobbler::writeSessionKey(const QString &sessionKey){
	m_crypter->encryptToFile(sessionKey, Settings::instance()->settingsDir() + "encrypted_session");
}

void Scrobbler::slotSettingsChanged(){
	printf("Scrobbler::slotSettingsChanged()\n");
	if(Settings::instance()->lastfm->isScrobbling == isActive())
		return;
	if(Settings::instance()->lastfm->isScrobbling)
		startSession();
	else
		stopSession();
}
